package triFileSystem;

import java.util.*;
import triStorage.*;
import transaction.*;
import utils.*;

public class PageDir implements Inode{

	private Vector<Integer> pageIds;
	private Vector<Integer> inodePageIds;
	
	private int metadataPageId;
	private int curPos;
	private int lastPageId;
	private TriMMPFile mmpfile;
	private TriMMPFile inodeFile;
	
	//private int NUM_FLAT_POINT = 2;
	//private static int NUM_ONELAYER_POINT = 2;
	
	private Object lock = new Object();
	
	public PageDir(int metadataPageId, TriMMPFile inodeFile, TriMMPFile mmpfile, Transaction tx) {
		this.mmpfile = mmpfile;
		this.inodeFile = inodeFile;
		
		this.metadataPageId = metadataPageId;
		lastPageId = -1;
		pageIds = new Vector<Integer> ();
		inodePageIds = new Vector<Integer> ();
		

		int pos = 0;
		//int length = mmpfile.readInt(metadataPageId, pos);
		int length = inodeFile.readInt(metadataPageId, pos);
		pos += Constants.INT_SIZE;
		
		for (int i = 0; i < length; ++i){
			//int k = mmpfile.readInt(metadataPageId, pos);
			int k = inodeFile.readInt(metadataPageId, pos);
			inodePageIds.add(k);
			pos += Constants.INT_SIZE;	
		}
		
		curPos = 0;
		if (inodePageIds.isEmpty()) 
			allocNewInodePageId(tx);
		loadPage();
	}
	
	public PageDir copy(Transaction tx) {
		return new PageDir(this.metadataPageId, this.inodeFile, mmpfile, tx);
	}
	
	private void loadPage() {
		if (curPos >= inodePageIds.size())
			return;
		
		int pageId = inodePageIds.elementAt(curPos++);
		int pos = 0;
		int length = mmpfile.readInt(pageId, pos);
		
		pos += Constants.INT_SIZE;
		for (int i = 0; i < length; ++i){
			pageIds.add(mmpfile.readInt(pageId, pos));
			pos += Constants.INT_SIZE;	
		}
	}
	
	public int getIthPageId(int ith) {
		synchronized (lock) {
			while (ith >= pageIds.size() && curPos < inodePageIds.size())
				loadPage();
			if (ith >= pageIds.size())
				return -1;

			return pageIds.elementAt(ith);
		}
	}
	
	public int getLastPageId() {
		synchronized (lock) {
			if (lastPageId >= 0)
				return lastPageId;
			if (inodePageIds.isEmpty())
				return -1;
			int pageId = inodePageIds.lastElement();
			int length = mmpfile.readInt(pageId, 0);
			if (length == 0)
				return -1;
			
			lastPageId = mmpfile.readInt(pageId, length << Constants.INT_SIZE_2POWER);
			return lastPageId;
		}
	}
	
	public void allocNewPageId(int pageId, Transaction tx) {
		synchronized (lock) {
			int lastInodePageId = inodePageIds.lastElement();
			int length = mmpfile.readInt(lastInodePageId, 0);
			int pos = (length + 1) << Constants.INT_SIZE_2POWER;
			
			if (mmpfile.PAGE_SIZE >= pos + Constants.INT_SIZE) {
				mmpfile.appendInt(lastInodePageId, pos, pageId, tx);
				mmpfile.writeInt(lastInodePageId, 0, length + 1, tx);
				//mmpfile.writeInt(lastInodePageId, pos, pageId);
				
				if (curPos >= inodePageIds.size())
					pageIds.add(pageId);
			}
			else {
				lastInodePageId = allocNewInodePageId(tx);
				mmpfile.appendInt(lastInodePageId, Constants.INT_SIZE, pageId, tx);
				mmpfile.writeInt(lastInodePageId, 0, 1, tx);
				//mmpfile.writeInt(lastInodePageId, Constants.INT_SIZE, pageId);
				
				if (curPos == inodePageIds.size() - 1) {
					curPos++;
					pageIds.add(pageId);
				}
			}
			
			lastPageId = pageId;
			
		}
	}
	
	private int allocNewInodePageId(Transaction tx) {
		int inodePageId = mmpfile.allocNewPage();
		inodePageIds.add(inodePageId);
		//mmpfile.writeInt(metadataPageId, inodePageIds.size() << Constants.INT_SIZE_2POWER, inodePageId);
		/*mmpfile.appendInt(metadataPageId, inodePageIds.size() << Constants.INT_SIZE_2POWER, 
				inodePageId, tx);
		mmpfile.writeInt(metadataPageId, 0, inodePageIds.size(), tx);*/
		inodeFile.appendInt(metadataPageId, inodePageIds.size() << Constants.INT_SIZE_2POWER, 
				inodePageId, tx);
		inodeFile.writeInt(metadataPageId, 0, inodePageIds.size(), tx);
		
		return inodePageId;
	}
	
}
